using Sensor.UI.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Media.Media3D;

namespace Sensor.UI.PointClouds;

public class PointCloudFactory
{
    #region 常量定义
    /// <summary>
    /// 预定义的区域索引数组
    /// </summary>
    private static readonly int[] DefaultRegionIndices = [674, 1824, 688, 660];
    
    /// <summary>
    /// 默认距离阈值比例（最小轴的百分比）
    /// </summary>
    private const double DefaultDistanceThresholdRatio = 0.15;
    #endregion

    #region 公共方法
    /// <summary>
    /// 创建分布区域模型
    /// </summary>
    /// <param name="a">椭球长半轴</param>
    /// <param name="b">椭球中半轴</param>
    /// <param name="c">椭球短半轴</param>
    /// <param name="verticalRings">垂直环数</param>
    /// <param name="horizontalRings">水平环数</param>
    /// <param name="distanceThreshold">点分配到区域的距离阈值，如果小于等于0则自动计算</param>
    /// <returns>包含分布区域的模型信息</returns>
    public static PointCloudInfo LoadModel(double a = 3.0, double b = 2.0, double c = 1.5, 
                                           int verticalRings = 15, int horizontalRings = 20, 
                                           double distanceThreshold = -1.0)
    {
        // 验证参数
        ValidateParameters(a, b, c, verticalRings, horizontalRings);

        // 生成椭球点网格
        var (allPoints, pointIndices) = GenerateEllipsoidPoints(a, b, c, verticalRings, horizontalRings);
        
        // 筛选满足条件的点(Y坐标<0且Z坐标>0)
        var positions = FilterValidPoints(allPoints, verticalRings, horizontalRings);

        // 创建点云信息对象
        var pointCloudInfo = CreatePointCloudInfo(pointIndices, allPoints);

        // 创建点信息并添加到集合
        AddPointInfos(pointCloudInfo, positions, a, b, c);

        // 自动计算距离阈值
        distanceThreshold = CalculateDistanceThreshold(a, b, c, distanceThreshold);

        // 创建区域信息
        CreateRegionInfos(pointCloudInfo, distanceThreshold);

        // 将点分配到各个区域
        AssignPointsToRegions(pointCloudInfo, distanceThreshold);
        
        // 创建线框点
        CreateWireframePoints(pointCloudInfo, verticalRings, horizontalRings);
        
        return pointCloudInfo;
    }
    #endregion

    #region 私有方法
    /// <summary>
    /// 验证输入参数
    /// </summary>
    private static void ValidateParameters(double a, double b, double c, int verticalRings, int horizontalRings)
    {
        if (a <= 0 || b <= 0 || c <= 0)
            throw new ArgumentException("椭球半轴必须大于0");
        
        if (verticalRings <= 0 || horizontalRings <= 0)
            throw new ArgumentException("环数必须大于0");
    }

    /// <summary>
    /// 生成椭球点网格
    /// </summary>
    private static (Point3D[,] allPoints, int[,] pointIndices) GenerateEllipsoidPoints(
        double a, double b, double c, int verticalRings, int horizontalRings)
    {
        var allPoints = new Point3D[verticalRings + 1, horizontalRings];
        var pointIndices = new int[verticalRings + 1, horizontalRings];
        int pointIndex = 0;

        // 生成所有点
        for (int v = 0; v <= verticalRings; v++)
        {
            double phi = Math.PI * v / verticalRings;
            double y = b * Math.Cos(phi);
            double currentRadius = Math.Sin(phi);

            for (int h = 0; h < horizontalRings; h++)
            {
                double theta = 2 * Math.PI * h / horizontalRings;
                double x = a * currentRadius * Math.Cos(theta);
                double z = c * currentRadius * Math.Sin(theta);
                allPoints[v, h] = new Point3D(x, y, z);
                pointIndices[v, h] = pointIndex++;
            }
        }

        return (allPoints, pointIndices);
    }

    /// <summary>
    /// 筛选满足条件的点(Y坐标<0且Z坐标>0)
    /// </summary>
    private static List<Point3D> FilterValidPoints(Point3D[,] allPoints, int verticalRings, int horizontalRings)
    {
        var positions = new List<Point3D>();
        
        for (int v = 0; v <= verticalRings; v++)
        {
            for (int h = 0; h < horizontalRings; h++)
            {
                var point = allPoints[v, h];
                if (point.Y < 0 && point.Z > 0)
                {
                    positions.Add(point);
                }
            }
        }
        
        return positions;
    }

    /// <summary>
    /// 创建点云信息对象
    /// </summary>
    private static PointCloudInfo CreatePointCloudInfo(int[,] pointIndices, Point3D[,] allPoints)
    {
        ArgumentNullException.ThrowIfNull(pointIndices);
        ArgumentNullException.ThrowIfNull(allPoints);
        
        var pointCloudInfo = new PointCloudInfo()
        {
            CameraInfo = new PointCloudCameraInfo()
            {
                LookDirection = new Vector3D(43.068, -24.490, -69.776),
                UpDirection = new Vector3D(0.053, -0.030, 0.998),
                Position = new Point3D(-24.974, 5.523, 38.429)
            },
            AllPoints = [],
            PointIndices = pointIndices
        };

        // 将所有点添加到 AllPoints 集合
        int verticalRings = allPoints.GetLength(0) - 1;
        int horizontalRings = allPoints.GetLength(1);
        
        for (int v = 0; v <= verticalRings; v++)
        {
            for (int h = 0; h < horizontalRings; h++)
            {
                pointCloudInfo.AllPoints.Add(allPoints[v, h]);
            }
        }

        return pointCloudInfo;
    }

    /// <summary>
    /// 创建点信息并添加到集合
    /// </summary>
    private static void AddPointInfos(PointCloudInfo pointCloudInfo, List<Point3D> positions, 
                                     double a, double b, double c)
    {
        ArgumentNullException.ThrowIfNull(pointCloudInfo);
        ArgumentNullException.ThrowIfNull(positions);
        
        foreach (var point in positions)
        {
            Vector3D normal = new(
                 point.X / (a * a),
                 point.Y / (b * b),
                 point.Z / (c * c)
             );

            // 归一化法向量
            normal.Normalize();

            pointCloudInfo.ItemInfos.Add(new PointCloudItemInfo { Point = point, Vector = normal });
        }
    }

    /// <summary>
    /// 计算距离阈值
    /// </summary>
    private static double CalculateDistanceThreshold(double a, double b, double c, double distanceThreshold)
    {
        if (distanceThreshold > 0)
            return distanceThreshold;
            
        // 基于椭球尺寸计算合适的阈值
        return Math.Min(Math.Min(a, b), c) * DefaultDistanceThresholdRatio;
    }

    /// <summary>
    /// 创建区域信息
    /// </summary>
    private static void CreateRegionInfos(PointCloudInfo pointCloudInfo, double distanceThreshold)
    {
        ArgumentNullException.ThrowIfNull(pointCloudInfo);
        
        var regionIndices = SelectSafeRegionIndices(pointCloudInfo.ItemInfos.Count);
        
        for (int i = 0; i < regionIndices.Length; i++)
        {
            var index = regionIndices[i];
            pointCloudInfo.RegionInfos.Add(new PointCloudRegionInfo 
            { 
                Index = i,
                RegionPoint = pointCloudInfo.ItemInfos[index],
                DistanceThreshold = distanceThreshold
            });
        }
    }

    /// <summary>
    /// 将点分配到各个区域
    /// </summary>
    private static void AssignPointsToRegions(PointCloudInfo pointCloudInfo, double distanceThreshold)
    {
        ArgumentNullException.ThrowIfNull(pointCloudInfo);
        
        var assignedPoints = new HashSet<PointCloudItemInfo>();

        foreach (var regionInfo in pointCloudInfo.RegionInfos)
        {
            // 找出距离该区域中心点在阈值内的所有点
            var nearbyPoints = pointCloudInfo.ItemInfos
                .Where(pointInfo => !assignedPoints.Contains(pointInfo))
                .Select(pointInfo => new 
                {
                    PointInfo = pointInfo,
                    Distance = CalculateDistance(regionInfo.RegionPoint.Point, pointInfo.Point)
                })
                .Where(x => x.Distance <= distanceThreshold)
                .OrderBy(x => x.Distance)
                .ToList();

            // 将找到的点添加到区域
            foreach (var item in nearbyPoints)
            {
                var regionItemInfo = new PointCloudRegionItemInfo
                {
                    Point = item.PointInfo.Point,
                    Vector = item.PointInfo.Vector,
                    Distance = item.Distance
                };

                regionInfo.ItemInfos.Add(regionItemInfo);
                assignedPoints.Add(item.PointInfo);
            }
        }

        // 从原始集合中移除已分配的点
        pointCloudInfo.ItemInfos.RemoveAll(x => assignedPoints.Contains(x));
    }

    /// <summary>
    /// 安全地选择区域索引，使用预定义的常量数组
    /// </summary>
    /// <param name="totalPoints">总点数</param>
    /// <returns>预定义的区域索引数组</returns>
    private static int[] SelectSafeRegionIndices(int totalPoints)
    {
        // 创建数组副本以避免修改常量数组
        var indices = new int[DefaultRegionIndices.Length];
        Array.Copy(DefaultRegionIndices, indices, DefaultRegionIndices.Length);
        
        // 确保索引不会越界
        for (int i = 0; i < indices.Length; i++)
        {
            if (indices[i] >= totalPoints)
            {
                indices[i] = Math.Max(0, totalPoints - 1);
            }
        }
        
        return indices;
    }

    /// <summary>
    /// 计算两点之间的距离
    /// </summary>
    /// <param name="point1">第一个点</param>
    /// <param name="point2">第二个点</param>
    /// <returns>两点之间的距离</returns>
    private static double CalculateDistance(Point3D point1, Point3D point2)
    {
        ArgumentNullException.ThrowIfNull(point1);
        ArgumentNullException.ThrowIfNull(point2);
        
        double dx = point1.X - point2.X;
        double dy = point1.Y - point2.Y;
        double dz = point1.Z - point2.Z;
        return Math.Sqrt(dx * dx + dy * dy + dz * dz);
    }

    /// <summary>
    /// 创建线框点集合
    /// </summary>
    private static void CreateWireframePoints(PointCloudInfo info, int verticalRings, int horizontalRings)
    {
        ArgumentNullException.ThrowIfNull(info);
        
        info.WireframePoints.Clear();

        if (info.PointIndices == null) return;

        // 生成水平线
        GenerateHorizontalLines(info, verticalRings, horizontalRings);

        // 生成垂直线
        GenerateVerticalLines(info, verticalRings, horizontalRings);
    }

    /// <summary>
    /// 生成水平线
    /// </summary>
    private static void GenerateHorizontalLines(PointCloudInfo info, int verticalRings, int horizontalRings)
    {
        ArgumentNullException.ThrowIfNull(info);
        
        if (info.AllPoints == null || info.PointIndices == null)
            return;
        
        for (int v = 0; v <= verticalRings; v++)
        {
            for (int h = 0; h < horizontalRings; h++)
            {
                int current = info.PointIndices[v, h];
                int nextH = info.PointIndices[v, (h + 1) % horizontalRings];

                if (current >= 0 && current < info.AllPoints.Count && 
                    nextH >= 0 && nextH < info.AllPoints.Count)
                {
                    var currentPoint = info.AllPoints[current];
                    var nextHPoint = info.AllPoints[nextH];

                    if (IsValidPointPair(currentPoint, nextHPoint))
                    {
                        info.WireframePoints.Add(currentPoint);
                        info.WireframePoints.Add(nextHPoint);
                    }
                }
            }
        }
    }

    /// <summary>
    /// 生成垂直线
    /// </summary>
    private static void GenerateVerticalLines(PointCloudInfo info, int verticalRings, int horizontalRings)
    {
        ArgumentNullException.ThrowIfNull(info);
        
        if (info.AllPoints == null || info.PointIndices == null)
            return;
        
        for (int v = 0; v < verticalRings; v++)
        {
            for (int h = 0; h < horizontalRings; h++)
            {
                int current = info.PointIndices[v, h];
                int nextV = info.PointIndices[v + 1, h];

                if (current >= 0 && current < info.AllPoints.Count && 
                    nextV >= 0 && nextV < info.AllPoints.Count)
                {
                    var currentPoint = info.AllPoints[current];
                    var nextVPoint = info.AllPoints[nextV];

                    if (IsValidPointPair(currentPoint, nextVPoint))
                    {
                        info.WireframePoints.Add(currentPoint);
                        info.WireframePoints.Add(nextVPoint);
                    }
                }
            }
        }
    }

    /// <summary>
    /// 检查点对是否满足条件(Y坐标<0且Z坐标>0)
    /// </summary>
    private static bool IsValidPointPair(Point3D point1, Point3D point2)
    {
        return point1.Y < 0 && point1.Z > 0 && point2.Y < 0 && point2.Z > 0;
    }
    #endregion
}
